package cn.maihe.elg.operation.schedule;

import cn.maihe.elg.operation.centers.hymake.auth.HyMakeCenterAuthConfig;
import cn.maihe.elg.operation.centers.hymake.config.HyMakeInfoConfig;
import cn.maihe.elg.operation.centers.hymake.enums.XMChannelMethod;
import cn.maihe.elg.operation.centers.supports.CenterInterfaceService;
import cn.maihe.elg.operation.centers.supports.CenterInterfaceServiceFactory;
import cn.maihe.elg.operation.centers.supports.model.bo.GuaranteeClaimSettlementResultBo;
import cn.maihe.elg.operation.centers.supports.model.bo.GuaranteeRevokeResultBo;
import cn.maihe.elg.operation.centers.supports.model.bo.PushCenterGuaranteeBo;
import cn.maihe.elg.operation.config.ElgConfig;
import cn.maihe.elg.operation.model.dto.InnerBaseResp;
import cn.maihe.elg.operation.model.enums.AcceptOrderStatus;
import cn.maihe.elg.operation.model.enums.CenterNoEnum;
import cn.maihe.elg.operation.model.enums.InnerRespCode;
import cn.maihe.elg.operation.model.enums.NoticeTypeEnum;
import cn.maihe.elg.operation.repository.entity.AcceptOrderInfoDO;
import cn.maihe.elg.operation.repository.entity.NoticeRetryInfo;
import cn.maihe.elg.operation.repository.entity.NoticeRetryInfoCondition;
import cn.maihe.elg.operation.repository.entity.SpdBankNotice;
import cn.maihe.elg.operation.repository.entity.SpdBankNoticeCondition;
import cn.maihe.elg.operation.service.business.ExchangeCenterService;
import cn.maihe.elg.operation.service.order.AcceptOrderInfoService;
import cn.maihe.elg.operation.service.order.NoticeRetryInfoService;
import cn.maihe.elg.operation.service.order.SpdBankNoticeService;
import cn.maihe.elg.operation.utils.ElgLogger;
import cn.maihe.elg.operation.utils.OkHttpUtils;
import cn.maihe.framework.starter.base.sql.PageBo;
import com.alibaba.fastjson.JSON;
import com.google.common.util.concurrent.ThreadFactoryBuilder;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections.CollectionUtils;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.util.Date;
import java.util.HashMap;
import java.util.Objects;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;

/**
 * @Description
 * @Author WJH
 * @Date 2021/08/10
 */

@Component
@Slf4j
public class NoticeRetrySendTask {

    private final NoticeRetryInfoService noticeRetryInfoService;
    private final ExecutorService executorService;
    private final AtomicBoolean running = new AtomicBoolean(false);

    @Resource
    private ExchangeCenterService exchangeCenterService;
    @Resource
    private ElgConfig elgConfig;
    @Resource
    private HyMakeInfoConfig hyMakeInfoConfig;

    @Resource
    private AcceptOrderInfoService acceptOrderInfoService;

    @Resource
    private SpdBankNoticeService spdBankNoticeService;

    public NoticeRetrySendTask(NoticeRetryInfoService noticeRetryInfoService) {
        this.noticeRetryInfoService = noticeRetryInfoService;
        ThreadFactory threadFactory = new ThreadFactoryBuilder().setNameFormat("NoticeRetry-%d").build();
        this.executorService = new ThreadPoolExecutor(8, 100, 0, TimeUnit.MILLISECONDS,
                new LinkedBlockingQueue<>(300), threadFactory);
    }

    @Scheduled(cron = "${cn.maihe.elg.operation.schedule.noticeRetryCron}")
    public void NoticeRetry() {
        try {
            if (running.compareAndSet(false, true)) {
                log.info("通知重试任务开始----");
                NoticeRetryInfoCondition condition = NoticeRetryInfoCondition.builder()
                        .andNextTimeLt(new Date()).andRetryTimesLe(5);
//                if(elgConfig.getEnableCenterId()!=null){
//                    condition.andCenterIdEq(elgConfig.getEnableCenterId());
//                }
                PageBo<NoticeRetryInfo> page = noticeRetryInfoService.selectPageBo(condition.build(), 1, 500);
                if (page != null && CollectionUtils.isNotEmpty(page.getList())) {
                    CompletableFuture[] cfs = page.getList().stream()
                            .map(this::doNoticeRetry).toArray(CompletableFuture[]::new);

                    //等待全部任务结束
                    CompletableFuture.allOf(cfs).join();
                }
                log.info("通知重试任务结束，共重试通知记录数[{}]", page.getTotal());
            } else {
                log.info("通知重试任务已开启执行中，等待其结束后重试");
            }
        } catch (Exception e) {
            log.error("通知重试任务结束：{}", e.getMessage(), e);
        } finally {
            running.compareAndSet(true, false);
        }

    }

    private CompletableFuture<InnerBaseResp> doNoticeRetry(NoticeRetryInfo noticeRetryInfo) {
        final NoticeTypeEnum type = NoticeTypeEnum.resolve(noticeRetryInfo.getNoticeType());
        final String acceptOrderNo = noticeRetryInfo.getAcceptOrderNo();

        return CompletableFuture.supplyAsync(() -> {
            CenterNoEnum centerNoEnum = exchangeCenterService.getCenterNoEnumById(noticeRetryInfo.getCenterId());
            CenterInterfaceService centerInterfaceService = CenterInterfaceServiceFactory.getByCenterNoEnum(centerNoEnum);
            String reqBodyJson = noticeRetryInfo.getReqBodyJson();
            InnerBaseResp innerBaseResp = null;
            if (type == NoticeTypeEnum.guarantee_apply_result) {
                PushCenterGuaranteeBo bo = JSON.parseObject(reqBodyJson, PushCenterGuaranteeBo.class);
                innerBaseResp = centerInterfaceService.pushGuarantee(bo);
                innerBaseResp.setReqObj(bo);
            } else if (type == NoticeTypeEnum.revoke_apply_result) {
                GuaranteeRevokeResultBo bo = JSON.parseObject(reqBodyJson, GuaranteeRevokeResultBo.class);
                bo.setAcceptOrderNo(acceptOrderNo);
                innerBaseResp = centerInterfaceService.noticeGuaranteeRevokeResult(bo);
                innerBaseResp.setReqObj(bo);
            } else if (type == NoticeTypeEnum.claims_apply_result) {
                GuaranteeClaimSettlementResultBo bo = JSON.parseObject(reqBodyJson, GuaranteeClaimSettlementResultBo.class);
                bo.setAcceptOrderNo(acceptOrderNo);
                innerBaseResp = centerInterfaceService.noticeGuaranteeClaimSettlementResult(bo);
                innerBaseResp.setReqObj(bo);
            }  else if (type == NoticeTypeEnum.push_hb_status) {
                log.info("pushHBStatus start");
                HashMap stringObjectMap = JSON.parseObject(reqBodyJson, HashMap.class);
                // 3.1.5 推送核保状态
                HyMakeCenterAuthConfig authConfig = hyMakeInfoConfig.getAuthConfig(CenterNoEnum.XIAMEN);
                innerBaseResp = OkHttpUtils.postDataByFormV2(authConfig.getCenterInfo().getUrl().trim() + XMChannelMethod.pushHBStatus.getMethod(), stringObjectMap,"3.1.5、推送核保状态");
                if (innerBaseResp.getRespCode().getRespCode().equals(InnerRespCode.SUCCESS.getRespCode())) {
                    SpdBankNotice dbSpdBankNotice = spdBankNoticeService.selectOne(SpdBankNoticeCondition.builder().andAcceptOrderNoEq(acceptOrderNo).build());
                    if(Objects.nonNull(dbSpdBankNotice)) {
                        AcceptOrderInfoDO acceptOrderInfo = acceptOrderInfoService.getByAcceptOrderNo(acceptOrderNo);
                        if(acceptOrderInfo.getStatus() == AcceptOrderStatus.WAIT_PAY_CONFIRM.getStatus()){
                            ElgLogger.info(log, log -> log.info("受理订单号[{}] >> 异步 解密修改自动出函", acceptOrderNo));
                            acceptOrderInfoService.updateGuarantee(acceptOrderInfo);
                        }
                    }
                }else {
                    ElgLogger.error(log, log -> log.error("受理订单号[{}] >> 异步 3.1.5 推送核保状态失败", acceptOrderNo));
                }
                innerBaseResp.setReqObj(stringObjectMap);
                log.info("pushHBStatus end");
            }else {
                innerBaseResp = InnerBaseResp.exception("暂未实现该通知类型的重试处理业务");
            }
            return innerBaseResp;
        }, executorService).whenCompleteAsync((innerBaseResp, t) -> {
            Object reqObj = innerBaseResp != null ? innerBaseResp.getReqObj() : null;
            if (t != null) {
                noticeRetryInfoService.saveOrUpdateNoticeRetryInfo(type,
                        noticeRetryInfo.getCenterId(), noticeRetryInfo.getOrgId(),
                        noticeRetryInfo.getAcceptOrderNo(), reqObj, null, innerBaseResp
                );
                ElgLogger.error(log, l -> l.error("受理订单号[{}] >> 异步推送[{}]重试异常:{}", acceptOrderNo, type, t.getMessage(), t));
            } else if (innerBaseResp != null && innerBaseResp.getRespCode().isSuccess()) {
                ElgLogger.info(log, log -> log.info("受理订单号[{}] >> 异步推送[{}]重试成功,响应报文：{}", acceptOrderNo, type, JSON.toJSONString(innerBaseResp)));
                noticeRetryInfoService.deleteByTypeAndOrderNo(type, acceptOrderNo);
            } else {
                noticeRetryInfoService.saveOrUpdateNoticeRetryInfo(type,
                        noticeRetryInfo.getCenterId(), noticeRetryInfo.getOrgId(),
                        noticeRetryInfo.getAcceptOrderNo(), reqObj, null, innerBaseResp
                );
                ElgLogger.info(log, log -> log.info("受理订单号[{}] >> 异步推送[{}]重试失败,待下次通知,响应报文：{}", acceptOrderNo, type, JSON.toJSONString(innerBaseResp)));
            }
        }, executorService);
    }


}
